
(function(){
	var PAD_GRID = 100;
	var PAD_RADIUS = 40;
	var OBJ_PREFIX = 'bbobj_';
	var OBJ_PREFIX_LEN = 6;

	var BBoardDesigner = G.GClass('APP', 'BBoardDesigner');
	BBoardDesigner.prototype.__init = function(board, w, h)
	{
		this.board = board;
		this.width = w || 5000;
		this.height = h || 3000;

		this._symUsage = {};

		this._objects = {};

		this.board.set_grid(100);
		this.board.view_grid(false);
		this.board.set_size(this.width, this.height);

		this._boardPad = this.board.def_symbol('board_pad_fill', 'pattern', 
			{
				width: PAD_GRID, height: PAD_GRID, patternUnits: 'userSpaceOnUse',
				'stroke': '222', 'fill': '#000'
			});
		this.board.add_element('rect', {x:0, y:0, width:PAD_GRID, height:PAD_GRID, 
					fill: '#030', 'stroke-width':0, 'stroke': '#030'}, this._boardPad);

		this.board.add_element('circle', {cx: 0, cy: 0, r: 24}, this._boardPad);
		this.board.add_element('circle', {cx: PAD_GRID, cy: 0, r: 24}, this._boardPad);
		this.board.add_element('circle', {cx: 0, cy: PAD_GRID, r: 24}, this._boardPad);
		this.board.add_element('circle', {cx: PAD_GRID, cy: PAD_GRID, r: 24}, this._boardPad);

		this.board.add_background('rect', {fill:'url(#board_pad_fill)', width:this.width, height: this.height});
		this.init_layers();
		this.board.zoom_fit();
	}

	BBoardDesigner.prototype.init_layers = function()
	{
		this._layers = {};
		this._layers['top'] = this.board.add_element('g', {'class': 'layerTop'});
		this._layers['bottom'] = this.board.add_element('g', {'class': 'layerBottom'});
		this._currentLayer = 'top';
	}

	BBoardDesigner.prototype.view_layer = function(l) 
	{
		if (l in this._layers) {
			if (l == 'top') {
				this._boardPad.setAttribute('stroke', '#222');
				this._boardPad.setAttribute('stroke-width', '0');
				this.board.mirror(false);
			} else {
				this._boardPad.setAttribute('stroke', '#880');
				this._boardPad.setAttribute('stroke-width', '20');
				this.board.mirror(true);
			}
			this.board.set_draw_context('layer', l);
			this.board.set_draw_context('container', this._layers[l]);
		}
		this._currentLayer = l;
		this.board.zoom_fit();
	}


	BBoardDesigner.prototype.switch_layer = function()
	{
		var l = this._currentLayer;
		if (l == 'top') {
			this.view_layer('bottom');
		} else if (l == 'bottom') {
			this.view_layer('top');
		}
	}


	BBoardDesigner.prototype.run = function() 
	{
		this.board.set_id_prefix(OBJ_PREFIX);
		this.board.load_hot_key({
			'p': {
					't': {func: this.mouse_draw_track, obj: this},
					'w': {func: this.mouse_draw_wire, obj: this},
					'c': {func: this.mouse_draw_component, obj: this},
					's': {func: this.mouse_draw_string, obj: this}
			},
			'd': {func: this.board.remove_selected, obj: this.board},
			'0': {func: this.board.zoom_fit, obj: this.board},
			'i': {func: this.board.zoom_in, obj: this.board},
			'o': {func: this.board.zoom_out, obj: this.board},
			'q': {func: this.end_draw, obj: this},
			'u': {func: this.board.undo, obj: this.board},
			'r': {func: this.board.redo, obj: this.board},
			'l': {func: this.switch_layer, obj: this},
			' ': {func: this.board.rotate, obj: this.board}
		});
	}

	var MOUSE_SELECT = {
		name: 'Select Mode',
		init: function() {
			this.board.deselect_all();
			this._draging = null;
		},
		onclick: function(pt, evt) {
			var o = pt.srcObject;

			if (evt.ctrlKey || evt.shiftKey) {
				if (this.board.is_selected(o.id)) {
					this.board.deselect(o.id);
					o = null;
				}
			} else {
				this.board.deselect_all();
			}

			if (o) {
				this.board.select(o.id);
			} 
		},
		onmousedown: function(pt, evt) {
			var o = pt.srcObject;
			console.log(o);
			if (!o) return;

			if (this.board.is_selected(o.id)) {
				console.log("start_drag");
				this.board.start_drag(pt);
			}
		},
		onmouseup: function(pt, evt) {
			this.board.end_drag();
		},
		deinit: function() {
			this.board.end_drag();
		}
	}




	/* ======================================================================
	 * Mouse interaction for Track ...
	 * ======================================================================
	 */
	var DRAW_TRACK = {
		name: 'Draw Track', 
		init: function() {
			this.board.init_draw_context();
		},
		onclick: function(pt, evt) {
			var track = this.board.in_drawing();

			if (track) {
				this.board.fly();	// Apply the draw context to the object
				this.add_object(this.board.draw_finish());
			} else {
				var tk = new G.APP.BBTrack(this.board);
				this.board.set_draw_context('pa', {x: pt.x, y: pt.y});
				this.board.fly(tk);
			}
		},
		onmousemove: function(pt, evt) {
			this.board.set_draw_context('pb', {x: pt.x, y: pt.y});
			this.board.fly();
		}
	}

	BBoardDesigner.prototype.mouse_draw_track= function()
	{
		DRAW_TRACK.obj = this;
		this.board.set_mouse_action(DRAW_TRACK);
	}


	/* ======================================================================
	 * Draw Wire...
	 * ======================================================================
	 */
	var DRAW_WIRE = {
		name: 'Draw Wire', 
		init: function() {
			this.board.init_draw_context();
		},
		onclick: function(pt, evt) {
			wire = this.board.in_drawing();
			if (wire) {
				this.board.fly();
				this.add_object(this.board.draw_finish());
			} else {
				this.board.set_draw_context('pa', {x: pt.x, y: pt.y});
				wire = new G.APP.BBWire(this.board);
				this.board.fly(wire);
			}
		},
		onmousemove: function(pt, evt) {
			this.board.fly();
		}
	}

	/* ======================================================================
	 * Draw String...
	 * ======================================================================
	 */
	var DRAW_STRING = {
		name: 'Draw String', 
		init: function() {
			this.board.init_draw_context();
		},
		onclick: function(pt, evt) {
			var txt = this.board.in_drawing();

			if (txt) {
				this.board.fly();
			} else {
				this.board.set_draw_context('pa', {x: pt.x, y: pt.y});
				txt = new G.SVG.ObjText(this.board, 'TEST1');
				this.board.fly(txt);
			}
		},
		onmousemove: function(evt) {
			this.board.fly();
		}
	}

	var PLACE_COMPONENT = {
		name: 'Place component',
		init: function() {
			this.board.init_draw_context();
			this.load_symbol(this._currentSymbol);
		},

		'onclick': function(pt, evt) {
			var obj = this.board.in_drawing();

			if (obj) {
				this.board.fly();
				this.add_object(this.board.draw_finish());
			} else {
				this.board.set_draw_context('pa', {x: pt.x, y: pt.y});
				obj = new BBComponent(this.board, this._currentSymbol.name);
				this.board.fly(obj);
			}
		},
		'onmousemove': function(pt, evt) {
			var obj = this.board.in_drawing();

			if (!obj) {
				this.board.set_draw_context('pa', {x: pt.x, y: pt.y});
				obj = new G.APP.BBComponent(this.board, this._currentSymbol.name);
				this.board.fly(obj);
			} else {
				if (obj.name != this._currentSymbol.name) {
					this.board.end_draw();
					this.load_symbol(this._currentSymbol);
					this.board.set_draw_context('pa', {x: pt.x, y: pt.y});
					obj = new G.APP.BBComponent(this.board, this._currentSymbol.name);
					this.board.fly(obj);
				} else {
					this.board.fly();
				}
			}
		}
	}

	BBoardDesigner.prototype.mouse_draw_wire= function()
	{
		DRAW_WIRE.obj = this;
		this.board.set_mouse_action(DRAW_WIRE);
	}

	BBoardDesigner.prototype.mouse_draw_string = function()
	{
		DRAW_STRING.obj = this;
		this.board.set_mouse_action(DRAW_STRING);
	}

	BBoardDesigner.prototype.mouse_draw_component = function()
	{
		var sym = this._currentSymbol;
		if (!sym) {
			alert('place select a component first.');
			return;
		}
		PLACE_COMPONENT.obj = this;
		this.board.set_mouse_action(PLACE_COMPONENT);
	}


	BBoardDesigner.prototype.add_object = function(obj)
	{
		this.board.add_object(obj);
	}


	BBoardDesigner.prototype.set_current_symbol = function(sym)
	{
		this._currentSymbol = sym;
	}

	BBoardDesigner.prototype.load_symbol = function(sym)
	{
		if (sym.name in this._symUsage) {
			return;
		}
		var def = this.board.def_symbol(sym.name, 'g');
		this._def_symbol(sym, def);

		this._symUsage[sym.name] = {count: 0, def: def};
	}

	BBoardDesigner.prototype.reload_symbol = function(sym)
	{
		if (sym.name in this._symUsage) {
			def = this._symUsage[sym.name].def;

			// Clear elements in symbol
			while(def.childNodes.length > 0) {
				def.removeChild(def.childNodes[0]);
			}

			this._def_symbol(sym, def);
		}
	}


	BBoardDesigner.prototype._def_symbol = function(sym, def)
	{
		var pads = sym.data.pads || [];
		var body = sym.data.body || [];

		var pd = this.board.add_element('g', {'class':'component_pads'}, def);
		var bd = this.board.add_element('g', {'class':'component_body'}, def);
		for (var i = 0; i < pads.length; i++) {
			var p = pads[i];
			var e = this.board.add_element('circle', {cx: p.x, cy: p.y, r:PAD_RADIUS }, pd);
		}
		for (var i = 0; i < body.length; i++) {
			var shp = body[i];
			this.board.add_element(shp.tag, shp.attr, bd);
		}
	}

	BBoardDesigner.prototype.end_draw = function() 
	{
		this.board.end_draw();
		MOUSE_SELECT.obj = this;
		this.board.set_mouse_action(MOUSE_SELECT);
	}


	/**
	 *
	 * @class BBObject
	 */
	var BBTrack = G.GClass('APP', 'BBTrack', G.SVG.Obj);
	BBTrack.prototype.__init = function(board)
	{
		this.__SVG__Obj__init(board);
	}
	BBTrack.prototype.render = function(context)
	{
		// Track can only draw on bottom layer.

		// Direction of track: 
		//   None: A
		//   H:    A----B
		//
		//   V:    A
		//         |
		//         B
		//
		//  HV:    A----C
		//              |
		//              B
		//
		// VH:    A
		//        |
		//        C----B
		//         
		var pa = context.pa;
		var pb = context.pb;
		var g = this._render_root('g', {'name': 'track'}, context);
		var lh = null;
		var lv = null;
		this.dir = '';

		var ca = this.board.add_element('circle', 
					{cx: pa.x, cy: pa.y, r: PAD_RADIUS}, g);
		if ((context.r == 0) || (context.r == 180)) {
			// Horizon first
			if (pa.x != pb.x) {	// Horizon Line
				lh = this.board.add_element('line', 
						{x1: pa.x, y1: pa.y, x2: pb.x, y2: pa.y}, g);
				this.dir += 'H';
				var ch = this.board.add_element('circle', 
						{cx: pb.x, cy: pa.y, r: PAD_RADIUS}, g);

			}
			if (pa.y != pb.y) {	// Vertical line
				lv = this.board.add_element('line', 
						{x1: pb.x, y1: pa.y, x2: pb.x, y2: pb.y}, g);
				this.dir += 'V';
				var cv = this.board.add_element('circle', 
						{cx: pb.x, cy: pb.y, r: PAD_RADIUS}, g);
			} 
		} else {
			// Virtical first
			if (pa.y != pb.y) {	// Vertical line
				lv = this.board.add_element('line', 
						{x1: pa.x, y1: pa.y, x2: pa.x, y2: pb.y}, g);
				this.dir += 'V';
				var cv = this.board.add_element('circle', 
						{cx: pa.x, cy: pb.y, r: PAD_RADIUS}, g);
			} 
			if (pa.x != pb.x) {	// Horizon Line
				lh = this.board.add_element('line', 
						{x1: pa.x, y1: pb.y, x2: pb.x, y2: pb.y}, g);
				this.dir += 'H';
				var ch = this.board.add_element('circle', 
						{cx: pb.x, cy: pb.y, r: PAD_RADIUS}, g);

			}
		}
		this.pa = {x: pa.x, y: pa.y};
		this.pb = {x: pb.x, y: pb.y};
		this.r = context.r;
	}


	BBTrack.prototype.fly = function(context)
	{
		if (this.element) {
			this.board.remove_element(this.element);
		}
		this.render(context);
	}

	BBTrack.prototype.drag = function(pt)
	{
		if (this.dir == 'H') {
		} else if (this.dir == 'L') {
		} else if (this.dir == '') {
		} else {
		}
	}


	BBTrack.prototype.to_json = function() {
		return {
			type: 'track',
			layer: 'bottom',
			pa: {x: this.pa.x, y: this.pa.y},
			pb: {x: this.pb.x, y: this.pb.y},
			r: this.r
		};
	}

	/**
	 * @class BBWire
	 */

	var BBWire = G.GClass('APP', 'BBWire', G.SVG.Obj);
	BBWire.prototype.__init = function(board)
	{
		this.__SVG__Obj__init(board);
	}

	BBWire.prototype.render = function(context)
	{
		var g = this._render_root('g', {'name': 'wire'}, context);
		this.board.add_element('line', 
				{x1: context.pa.x, y1: context.pa.y, x2: context.pb.x, y2: context.pb.y}, g);
		this.board.add_element('circle',
				{cx: context.pa.x, cy: context.pa.y, r: PAD_RADIUS}, g);
		this.board.add_element('circle',
				{cx: context.pb.x, cy: context.pb.y, r: PAD_RADIUS}, g);
	}

	BBWire.prototype.fly = function(context)
	{
		if (!this.element)	return;

		var ln = this.element.childNodes[0];
		var padb = this.element.childNodes[2];
		var p = context.pb;
		ln.setAttribute('x2', p.x);
		ln.setAttribute('y2', p.y);
		padb.setAttribute('cx', p.x);
		padb.setAttribute('cy', p.y);

		this.pa = {x: context.pa.x, y: context.pa.y};
		this.pb = {x: context.pb.x, y: context.pb.y};
		this.layer = context.layer;
	}

	BBWire.prototype.to_json = function()
	{
		return {
				type: 'wire',
				layer: this.layer,
				pa: {x: this.pa.x, y: this.pa.y},
				pb: {x: this.pb.x, y: this.pb.y}
		}
	}

	var BBComponent = G.GClass('APP', 'BBComponent', G.SVG.Obj);
	BBComponent.prototype.__init = function(board, symName)
	{
		this.__SVG__Obj__init(board);
		this.name = symName;
	}

	BBComponent.prototype.render = function(context) 
	{
		var attr = { };
		if (context.r != 0) {
			attr.transform = 'rotate(' + context.r 
					+ ',' + context.pa.x  
					+ ',' + context.pa.y + ')';
		}
		attr.x = context.pa.x;
		attr.y = context.pa.y;
		var g = this._render_root('g', {}, context);
		var u = this.board.add_use(this.name, attr, g);

		this.use = u;
		this.x = context.pb.x;
		this.y = context.pb.y;
		this.r = context.r;
		this.layer = context.r;
	}

	BBComponent.prototype.fly = function(context)
	{
		if (this.use) {
			transform = 'rotate(' + context.r 
					+ ',' + context.pb.x  
					+ ',' + context.pb.y + ')';
			this.use.setAttribute('x', context.pb.x);
			this.use.setAttribute('y', context.pb.y);
			this.use.setAttribute('transform', transform);
		}
		this.x = context.pb.x;
		this.y = context.pb.y;
		this.r = context.r;
		this.layer = context.r;
	}

	BBComponent.prototype.drag = function(ctx)
	{
		this.x = ctx.pb.x;
		this.y = ctx.pb.y;

		var transform = 'rotate(' + this.r + ',' + this.x  + ',' + this.y + ')';
		this.use.setAttribute('x', this.x);
		this.use.setAttribute('y', this.y);
		this.use.setAttribute('transform', transform);
	}

	BBComponent.prototype.to_json = function()
	{
		return {
			type: 'component',
			symName: this.name,
			pa: {x: this.x, y: this.y},
			r: this.r,
			layer: this.layer
		}
	}

 })();

